package com.adun.scheduler;

import com.adun.scheduler.vo.SchedulerJobVo;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import lombok.Data;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ZDF
 * @date 2025/8/8
 */
public class SchedulerJobManager implements ApplicationContextAware {

    private static final Logger log = LoggerFactory.getLogger(SchedulerJobManager.class);


    private static final String XXL_JOB_TASK_INIT_SQL_TEMPLATE = "INSERT INTO `xxl_job_info` " +
            "(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, " +
            "`schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`," +
            " `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, " +
            "`child_jobid`, `trigger_status`, `trigger_last_time`, `trigger_next_time`) VALUES" +
            " ({id}, 2, '{jobDesc}', '{addTime}', '{updateTime}', 'basicScheduler', '', 'CRON', '{scheduleConf}', 'DO_NOTHING', 'ROUND', '{executorHandler}'," +
            " '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2025-08-09 11:57:42', '', 0, 0, 0);\n";

    private static final String XXL_JOB_EXECUTOR_INIT_SQL_TEMPLATE = "INSERT INTO `xxl_job_group` (`id`, `app_name`, `title`, `address_type`, `address_list`, `update_time`) VALUES " +
            "(2, 'basic-scheduler-executor', '基础scheduler执行器', 0, NULL, '${now!}');\n";

    private static ApplicationContext applicationContext;

    /**
     * 标准日期时间格式，精确到秒：yyyy-MM-dd HH:mm:ss
     */
    public static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * $ {param!}
     */

    private static final Pattern TEMPLATE_ARG_PATTERN = Pattern.compile("\\$\\{(.+?)!}");

    private static String now() {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(NORM_DATETIME_PATTERN);
        LocalDateTime now = LocalDateTime.now();
        return dateTimeFormatter.format(now);
    }

    /**
     * 格式化模板,正则
     *
     * @param template
     * @param argMap
     * @return
     */
    private static String format(String template, Map<String, String> argMap) {
        Matcher m = TEMPLATE_ARG_PATTERN.matcher(template);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            String arg = m.group(1);
            String replaceStr = argMap.get(arg);
            m.appendReplacement(sb, replaceStr != null ? replaceStr : "");
        }
        m.appendTail(sb);
        return sb.toString();
    }


    public static List<ISchedulerJob> getSchedulerJobs() {
        Map<String, ISchedulerJob> schedulerJobHandlerMap = applicationContext.getBeansOfType(ISchedulerJob.class);
        if (CollectionUtils.isEmpty(schedulerJobHandlerMap)) {
            return new ArrayList<>();
        }
        return new ArrayList<>(schedulerJobHandlerMap.values());
    }

    public static List<SchedulerJobVo> getSchedulerJobVos() {
        Map<String, ISchedulerJob> schedulerJobHandlerMap = applicationContext.getBeansOfType(ISchedulerJob.class);
        if (CollectionUtils.isEmpty(schedulerJobHandlerMap)) {
            return new ArrayList<>();
        }
        List<SchedulerJobVo> schedulerJobVos = new ArrayList<>();
        for (Map.Entry<String, ISchedulerJob> entry : schedulerJobHandlerMap.entrySet()) {
            SchedulerJobVo schedulerJobVo = new SchedulerJobVo();
            String handlerJobName = entry.getValue().getJobName();
            if (StringUtils.isBlank(handlerJobName)) {
                // 默认使用beanName
                handlerJobName = entry.getKey();
            }
            schedulerJobVo.setJobName(handlerJobName);
            schedulerJobVo.setJobDescription(entry.getValue().getJobDescription());
            schedulerJobVo.setJobCron(entry.getValue().getJobCron());
            String xxlJobCron = getXxlJobCron(entry.getValue().getJobCron());
            schedulerJobVo.setXxlJobCron(xxlJobCron);
            schedulerJobVo.setJobBeanName(entry.getKey());
            if (ObjectUtils.isNotEmpty(entry.getValue().getJobClassType())) {
                schedulerJobVo.setJobClassName(entry.getValue().getJobClassType().getName());
            }
            schedulerJobVos.add(schedulerJobVo);
        }
        return schedulerJobVos;
    }

    public static String getXxlJobExecutorInitSQL() {
        String now = now();
        HashMap<String, String> argsMap = new HashMap<String, String>() {{
            put("now", now);
        }};
        return format(XXL_JOB_EXECUTOR_INIT_SQL_TEMPLATE, argsMap);
    }

    public static List<String> getXxlJobTaskInitSQL() {
        List<ISchedulerJob> schedulerJobs = getSchedulerJobs();
        if (CollectionUtils.isEmpty(schedulerJobs)) {
            return new ArrayList<>();
        }
        List<String> xxlJobTaskInitSQLs = new ArrayList<>();
        for (int i = 0; i < schedulerJobs.size(); i++) {
            ISchedulerJob schedulerJob = schedulerJobs.get(i);
            XxlJobTaskFillBo xxlJobTaskFillBo = new XxlJobTaskFillBo();
            xxlJobTaskFillBo.setId(String.valueOf(i + 1));
            String jobDescription = schedulerJob.getJobDescription();
            xxlJobTaskFillBo.setJobDesc(StringUtils.isNotBlank(jobDescription) ? jobDescription : schedulerJob.getJobName());
            xxlJobTaskFillBo.setAddTime(now());
            xxlJobTaskFillBo.setUpdateTime(now());
            String xxlJobCron = getXxlJobCron(schedulerJob.getJobCron());
            xxlJobTaskFillBo.setScheduleConf(xxlJobCron);
            xxlJobTaskFillBo.setExecutorHandler(schedulerJob.getJobName());
            Map<String, Object> map = beanToMap(xxlJobTaskFillBo);
            Map<String, String> argsMap = new HashMap<>(map.size());
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                argsMap.put(entry.getKey(), entry.getValue().toString());
            }
            String sql = format(XXL_JOB_TASK_INIT_SQL_TEMPLATE, argsMap);
            xxlJobTaskInitSQLs.add(sql);
        }
        return xxlJobTaskInitSQLs;
    }

    private static String getXxlJobCron(String jobCron) {
        if (StringUtils.isBlank(jobCron)) {
            log.warn("jobCron为空");
            return null;
        }
        String[] items = jobCron.split(" ");
        if (items.length < 6) {
            log.error("jobCron格式错误,jobCron:{}", jobCron);
            throw new RuntimeException("jobCron格式错误");
        }
        if (items.length == 7) {
            return jobCron;
        }
        return items[0] + " " + items[1] + " " + items[2] + " " + items[3] + " " + items[4] + " " + "?" + " " + items[5];
    }

    public static Map<String, Object> beanToMap(Object bean) {
        if (ObjectUtils.isEmpty(bean)) {
            return new HashMap<>();
        }
        return JSON.parseObject(JSON.toJSONString(bean), new TypeReference<Map<String, Object>>() {
        });
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SchedulerJobManager.applicationContext = applicationContext;
    }


    @Data
    public static class XxlJobTaskFillBo {
        /**
         * id 临时填充id
         */
        private String id;

        /**
         * 任务描述
         */
        private String jobDesc;

        /**
         * 添加时间
         */
        private String addTime;

        /**
         * 更新时间
         */
        private String updateTime;

        /**
         * 调度配置cron
         */
        private String scheduleConf;

        /**
         * 执行器 job bean名称
         */
        private String executorHandler;
    }
}
